package br.edu.catolica.sadbc.core.util;

import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;

import org.apache.commons.lang.time.DateFormatUtils;

public class DataUtil {	
	
	private static Pattern PATTERN_HORARIO = Pattern.compile("\\d{2}\\:\\d{2}");
	
	/**
	 * Instancia �nica da classe
	 */
	private static DataUtil instance = new DataUtil();
	
	private DataUtil(){}
	
	public static DataUtil getInstance(){
		return instance;
	}
	
	/**
	 * Retorna a data atual
	 * @param 
	 * @return
	 */
	public Date getDataAtual() {
		return getCalendar().getTime();
	}

	/**
	 * Retorna a data atual formatada como dd/MM/yyyy.
	 * @param data
	 * @return
	 */
	public String getDataFormatada(Date data) {
		String dataFormatada = null;
		SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");

		if (data != null) {
			dataFormatada = sdf.format(data);
		}
			
		return dataFormatada;		
	}
	
	public String getDataFormatada(Date data, SimpleDateFormat format) {
		String dataFormatada = null;
		if (data != null) {
			dataFormatada = format.format(data);
		}
		return dataFormatada;
	}

	/**
	 * Retorna a hora formatada como HH:mm.
	 * @param 
	 * @return
	 */
	public static String getHoraFormatada(Date data) {
		String dataFormatada = null;
		
		if (data != null) {
			dataFormatada = new SimpleDateFormat("HH:mm").format(data);
		}
			
		return dataFormatada;		
	}

	
	 /**
     * Recebe data(dd/MM/yyyy) e horario (HH:mm), retorna um Date     
     * @param data
     * @param horario
     * @return
     */
    public Date getDataComHorario(String data, String horario) {
    	Calendar c = Calendar.getInstance();
    	
    	c.setTime(toDate(data)); 
    	c.set(Calendar.HOUR_OF_DAY, getHoras(horario));
    	c.set(Calendar.MINUTE, getMinutos(horario));
    	
    	return c.getTime();
    }

    
	/**
	 * Verifica se o hor�rio � v�lido(horas entre 0 e 23 e minutos entre 0 e 59)
	 * @param horarioString
	 * @return
	 */
	public boolean isHorarioValido(String horarioString) {
		boolean flag = false;
		
		if (isFormatacaoHorarioValida(horarioString)) {
			int hora = getHoras(horarioString);
			int minuto = getMinutos(horarioString);
			
			flag = (((hora >= 0) && (hora <= 23)) && ((minuto >= 0) && (minuto <= 59)));
		}		
		
		return flag;
	}
	
	/**
	 * Verifica se o parametro informado refere-se a uma data valida. 
	 * @param parameterValue
	 * @return
	 */
	public boolean isDate(String parameterValue) {
		boolean isDate = false;
		
		try {
			new SimpleDateFormat("dd/MM/yyyy").parse(parameterValue);
			isDate = true;
		} catch (ParseException e) {
			isDate = false;
		}
		
		return isDate;
	}
	
	/**
	 * Verifica se o hor�rio formatado � v�lido
	 * @param horarioString
	 * @return
	 */
	public boolean isFormatacaoHorarioValida(String horarioString) {
		return PATTERN_HORARIO.matcher(horarioString).matches();
	}


    
	/**
	 * Recebe um <code>java.lang.String</code> e transforma para um Date no formato (dd/MM/YYYY)
	 * @param dataStr
	 * @return
	 */
	public static Date toDate(String dataStr) {
		Date data = null;
		try {
			if (!StringUtil.getInstance().isVazia(dataStr)) {
				SimpleDateFormat df = criarSimpleDateFormat();
				df.setLenient(false);
				data = df.parse(dataStr);
			}
		} catch (Exception e) {
			throw new IllegalArgumentException("Erro formatando data [" + dataStr + "].");
		}

		return data;
	}
	
	 /**
     * Recebe um Date e retorna a Descri��o do m�s em Portugu�s     
     * @param data 
     * @return
     */
	public String getMesPorExtenso(Date data){
		return formatDate(data, "MMMM");
	}
	
	/**
     * Recebe um Date e retorna a descri��o do dia em Portugu�s
     * Ex.: 'Segunda-feira'    
     * @param data 
     * @return
     */
	public String getDiaPorExtenso(Date data){
		return formatDate(data,"EEEE");
	}

	/**
	 * Retorna a data por extenso em Portugu�s utilizando o padr�o informado 
	 * @param data
	 * @param pattern
	 * @return
	 */
	public String formatDate(Date data, String pattern) {
		return DateFormatUtils.format(data, pattern, new Locale("pt"));
	}
	
	/**
	 * Adiciona dias a uma data
	 * @param data
	 * @param dias
	 * @return
	 */
	public Date adicionarDias(Date data, int dias) {
		Calendar calendar = getCalendar();
		
		calendar.setTime(data);
		calendar.add(Calendar.DAY_OF_MONTH, dias);
		
		Date dataRetorno = calendar.getTime();
		
		return dataRetorno;
	}
	

	/**
	 * Adiciona meses a uma data
	 * @param data
	 * @param dias
	 * @return
	 */
	public Date adicionarMeses(Date data, int meses) {
		Calendar calendar = getCalendar();
		
		calendar.setTime(data);
		calendar.add(Calendar.MONTH, meses);
		
		return calendar.getTime();
	}
	
	/**
	 * Adiciona anos a uma data
	 * @param data
	 * @param dias
	 * @return
	 */
	public Date adicionarAnos(Date data, int anos) {
		Calendar calendar = getCalendar();
		
		calendar.setTime(data);
		calendar.add(Calendar.YEAR, anos);
		
		return calendar.getTime();
	}
	
	/**
	 * Adiciona semanas a uma data
	 * @param data
	 * @param dias
	 * @return
	 */
	public Date adicionarSemanas(Date data, int semanas) {
		Calendar calendar = getCalendar();
		
		calendar.setTime(data);
		calendar.add(Calendar.WEEK_OF_MONTH, semanas);
		
		return calendar.getTime();
	}
	
	
	/**
	 * Verifica se a dataA � anterior � dataB sem considerar a hora;
	 * @param dataA
	 * @param dataB
	 * @return
	 */
	public boolean isDataMenor(Date dataA, Date dataB ) {
		return zeraCalendar(dataA).before(zeraCalendar(dataB)); 
	}
	
	/**
	 * Verifica se a dataA � posterior � dataB sem considerar a hora;
	 * @param dataA
	 * @param dataB
	 * @return
	 */
	public boolean isDataMaior(Date dataA, Date dataB ) {
		return zeraCalendar(dataA).after(zeraCalendar(dataB)); 
	}
	
	/**
	 * Verifica se a dataA � igual � dataB sem considerar a hora
	 * @param dataA
	 * @param dataB
	 * @return
	 */
	public boolean isDataIgual(Date dataA, Date dataB ) {
		return zeraCalendar(dataA).equals(zeraCalendar(dataB)); 
	}	

	/**
	 * Recupera a diferen�a em dias das datas passadas como par�metro
	 * @param dataA
	 * @param dataB
	 * @return
	 */
	public int diferencaEmDias(Date dataA, Date dataB) {
        int diferencaTemporaria = 0;
        int diferencaEmDias = 0;
        Calendar menorData = Calendar.getInstance();
        Calendar maiorData = Calendar.getInstance();
        if (dataA.compareTo(dataB) < 0) {
            menorData.setTime(dataA);
            maiorData.setTime(dataB);
        } else {
            menorData.setTime(dataB);
            maiorData.setTime(dataA);
        }
        while (menorData.get(Calendar.YEAR) != maiorData.get(Calendar.YEAR)) {
            diferencaTemporaria = 365 * (maiorData.get(Calendar.YEAR) - menorData.get(Calendar.YEAR ));
            diferencaEmDias += diferencaTemporaria;
            menorData.add(Calendar.DAY_OF_YEAR, diferencaTemporaria);
        }
        if (menorData.get(Calendar.DAY_OF_YEAR) != maiorData.get(Calendar.DAY_OF_YEAR )) {
            diferencaTemporaria = maiorData.get(Calendar.DAY_OF_YEAR)
                    - menorData.get(Calendar.DAY_OF_YEAR);
            diferencaEmDias += diferencaTemporaria;
            menorData.add (Calendar.DAY_OF_YEAR, diferencaTemporaria);
        }
        return diferencaEmDias;
    }
	
	/**
	 * Zera as horas e retorna somente a data.
	 * @param data
	 * @return
	 */
	public Date zeraCalendar(Date data) {
		Calendar calendar = getCalendarInstance();
        calendar.setTime(data);
		calendar.set(Calendar.MILLISECOND, 0);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar.getTime();
    }
	
	/**
	 * Retorna a data com o hora de 23:59:59
	 * @return Date
	 */
	
	public Date getDataHoraMaximaDia(Date data) {
		Calendar calendar = getCalendarInstance();
        calendar.setTime(data);
		calendar.set(Calendar.MILLISECOND, 23);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        return calendar.getTime();
    }
	
	/**
	 * Recupera a data atual com a hora zerada
	 * @return
	 */
	public Date getDataAtualHoraZero() {
        return zeraCalendar(getDataAtual());
    }
	
 
	private Calendar getCalendarInstance() {
			return Calendar.getInstance();
	}

	
	/**
	 * Verifica se as datas s�o maiores que um intervalo X em meses.
	 *
	 * @param dataInicio
	 * @param dataFim
	 * @param intervalo
	 * @return
	 */
	public boolean isIntervaloMaior(Date dataInicio, Date dataFim, int intervalo){
		Date dataComp = adicionarMeses(dataInicio, intervalo);
		
		return dataComp.before(dataFim);		
	}
	
	/**
	 * Retorna o ano
	 * @param data
	 * @return
	 */
	public int getAno(Date data){
		Calendar calendarioData = Calendar.getInstance();
		calendarioData.setTime(data);
		return calendarioData.get(Calendar.YEAR);
	}
	
	/**
	 * Retorna o Dia do M�s
	 * @param data
	 * @return
	 */
	public String getDia(Date data){
		Calendar calendarioData = Calendar.getInstance();
		calendarioData.setTime(data);
		
		return String.valueOf(calendarioData.get(Calendar.DAY_OF_MONTH));
	}
	
	/**
	 * Retorna o M�s
	 * @param data
	 * @return
	 */
	public String getMes(Date data){
		Calendar calendarioData = Calendar.getInstance();
		calendarioData.setTime(data);
		
		return String.valueOf(calendarioData.get(Calendar.MONTH));
	}
	
	private int getMinutos(String horarioString) {
		return Integer.parseInt(horarioString.substring(3, horarioString.length()));
	}

	private int getHoras(String horarioString) {
		return Integer.parseInt(horarioString.substring(0, 2));
	}

	private static  SimpleDateFormat criarSimpleDateFormat() {
		return new SimpleDateFormat("dd/MM/yyyy");
	}

	private Calendar getCalendar() {
		return Calendar.getInstance();
	} 

    /**
     * @param date
     * @return
     */
    static public String BrasilianDate2String(Date date) {
        DateFormat simpleDateFormat = new SimpleDateFormat("dd.MM.yyyy");
        String result = simpleDateFormat.format(date);
        return result;
    }

    /**
     * @param string
     * @return
     */
    static public Date String2BrasilianDate(String string) {
        Date result = new Date();
        if (!string.equals("")) {
            if (!string.equals("31.12.9999")) {
            	DateFormat simpleDateFormat = new SimpleDateFormat("dd.MM.yyyy");
            	try {
            		result = simpleDateFormat.parse(string);
            	} catch (ParseException e) {
            		e.printStackTrace();
            	}
            }else{
            	result = null;
            }
        }
        return result;
    }
    
    /**
     * @param string
     * @return
     */
    static public Date String2BrasilianDateTraco(String string) {
        Date result = new Date();
        if (!string.equals("")) {
            if (!string.equals("9999-12-31")) {
            	DateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            	try {
            		result = simpleDateFormat.parse(string);
            	} catch (ParseException e) {
            		e.printStackTrace();
            	}
            }else{
            	result = null;
            }
        }
        return result;
    }
    
    static public Timestamp stringToTimestamp(String string){
    	DateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss.SSSSSS");
    	Date result = null;
    	try {
    		result = simpleDateFormat.parse(string);
    	} catch (ParseException e) {
    		e.printStackTrace();
    	}
    	Timestamp ts = new Timestamp(result.getTime());
    	return ts;
    }
    
   

    /**
	 * 
	 * @param dt
	 * @return
	 */
    static public Date formatDate(String dt) {

		SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");

		Date data = null;
		try {
			data = sdf.parse(dt);

		} catch (ParseException e) {

			e.printStackTrace();
		}

		return data;
	}
    
    /**
     * Recupera lista de Dates contendo os anos anteriores ao ano atual,
     * de acordo com o parametro passado.
     * 
     * @author Guilherme Broock - BRQ Curitiba
     * @param  numberYears
     * @return List<Date>
     */
    static public List<Date> getBeforeYears(int numberYears){
    	
    	List<Date> dates= new ArrayList<Date>();
    	GregorianCalendar gc = new GregorianCalendar();
		dates.add(gc.getTime());
		
		for(int i=0; i<numberYears; i++){
			gc.add(GregorianCalendar.YEAR, -1);
			dates.add(gc.getTime());
		}
    	return dates;
    }
    
    /**
     * Recupera o ano de um Date.
     * 
     * @author Guilherme Broock - BRQ Curitiba
     * @param  date
     * @return String
     */
    static public String getYear(Date date) {
	    SimpleDateFormat format = new SimpleDateFormat("yyyy");  
	    String strYear = format.format(date);
		return strYear;
    }
}
